package com.heima_juc.chapter2_线程;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

import static com.heima_juc.util.Sleeper.sleep;

/**
 * @author linjy on 2022/11/1
 */
@Slf4j(topic = "c.TestInterrupt")
public class Interrupt中断 {
    @Test
    public void test1() throws InterruptedException {
        //1、sleep()、wait()、join()调用这三个方法可以使得线程阻塞，线程阻塞之后，cpu是不会分配时间片的。
        //2、interrupt()方法：
        //  作用：打断线程。
        //  (1)、当使用interrupt()方法去打断处于阻塞状态的线程时，线程将立即退出被阻塞状态，会抛出异常，而不会更新打断标记(就是会清空打断标记)，
        //  因此，虽然被打断，但是打断标记依然为false。
        //  (2)、当使用interrupt()方法去打断正在运行线程时，被打断的线程会继续运行，不会抛出异常，但是该线程的打断标记会更新，更新为true，
        //  因此可以根据打断标记来作为判断条件使得线程停止。

        //isInterrupted判断完后不会清除打断标记，但是interrupted方法判断完后会清除

        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        sleep(0.5);

        log.debug(" 当前线程状态:{}",t1.getState());
        log.debug(" 打断前打断状态: {}", t1.isInterrupted());
        log.debug(" 当前线程状态:{}",t1.getState());
        //log.debug(" 打断前打断状态--: {}", t1.interrupted());

        t1.interrupt();
        sleep(0.5);//为什么要休眠?应该是因为t1.interrupt();是个异步操作

        log.debug(" 打断后打断状态: {}", t1.isInterrupted());
        log.debug(" 当前线程状态:{}",t1.getState());
        //log.debug(" 打断后打断状态--: {}", t1.interrupted());
    }


    /**
     *  isInterrupted判断完后不会清除打断标记，但是interrupted方法判断完后会清除
     *
     * (2)、当使用interrupt()方法去打断正在运行线程时，被打断的线程会继续运行，不会抛出异常，
     * 但是该线程的打断标记会更新，更新为true，
     * 因此可以根据打断标记来作为判断条件使得线程停止。
     */
    @Test
    public void test2_1() throws InterruptedException {
        Thread t2 = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                if (interrupted) {
                    log.debug(" 打断状态: {}", interrupted);
                    log.debug(" 打断状态: {}", interrupted);
                    break;
                }
            }
        }, "t2");
        t2.start();

        sleep(0.5);
        t2.interrupt();
        //log.debug(" 打断状态1: {}", t2.isInterrupted());//true
        //log.debug(" 打断状态2: {}", t2.isInterrupted());//true

        sleep(1);//防止 打断状态那行不输出
    }

    /**
     * interrupted和isInterrupted的区别
     * 相同点：都是判断当前线程是否被打断
     * 不同点：
     * interrupted()只能对当前线程调用，调用方法Thread.currentThread().interrupted() (好像都可以)
     * 会清空原有的interrupt状态，比如输出两次线程状态，第一次是true，第二次是false
     */
    @Test
    public void test2_2() throws InterruptedException {
        Thread t2 = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                if (interrupted) {
                    //log.debug(" 打断状态: {}", interrupted);
                    log.debug(" 打断状态: {}", current.interrupted());//true
                    log.debug(" 打断状态: {}", current.interrupted());//false
                    break;
                }
            }
        }, "t2");
        t2.start();

        sleep(0.5);
        t2.interrupt();
        //log.debug(" 打断状态1: {}", t2.interrupted());

        sleep(1);//防止 打断状态那行不输出
    }

    /**
     * 打断 park 线程，让线程继续往下执行。不会清空打断状态，仍为true
     * 与另外三种阻塞状态不同(sleep()、wait()、join())，这三种会抛出异常，并且会清空打断状态，为false状态
     */
    @Test
    public void test3() {
        Thread t1 = new Thread(() -> {
            log.debug("park...");
            LockSupport.park();
            log.debug("unpark...");
            log.debug("打断状态1：{}", Thread.currentThread().isInterrupted());//true
            //log.debug("打断状态2：{}", Thread.currentThread().isInterrupted());//true
        }, "t1");
        t1.start();


        sleep(2.5);
        t1.interrupt();
    }

    /**
     * 如果打断标记已经是 true, 则 park 会失效。
     * 可以使用 Thread.interrupted()， 清除打断状态，为false状态，只会运行1次
     * 使用  Thread.currentThread().isInterrupted()， 不会清除打断状态，为true状态，会运行5次
     */
    @Test
    public void test4() {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                log.debug("park...");
                LockSupport.park();
                //log.debug("打断状态interrupted()1：{}", Thread.interrupted());//true
                //log.debug("打断状态interrupted()2：{}", Thread.interrupted());//false

                log.debug("打断状态isInterrupted()1：{}", Thread.currentThread().isInterrupted());
            }
        });
        t1.start();


        sleep(1);
        t1.interrupt();

    }

    /**
     * 摘自Java高并发编程详解(p63)
     */
    @Test
    public void test5() throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        TimeUnit.MINUTES.sleep(1);
                    } catch (InterruptedException e) {
                        //e.printStackTrace();
                        System.out.printf("I am be interrupted ? %s \n", isInterrupted());
                    }
                }
            }
        };
        thread.setDaemon(true);
        thread.start();
        TimeUnit.MILLISECONDS.sleep(2);
        System.out.printf("Thread is interrupted1 ? %s\n", thread.isInterrupted());
        thread.interrupt();//thread.interrupt()是个异步操作by linjy
        TimeUnit.MILLISECONDS.sleep(2);//TODO 不知道这里去掉休眠,为什么对结果产生影响,应该是thread.interrupt()是个异步操作by linjy
        System.out.printf("Thread is interrupted2 ? %s\n", thread.isInterrupted());
    }

    /**
     * 摘自Java高并发编程详解(p66)
     */
    @Test
    public void test6() throws InterruptedException {
        //①判断当前线程是否被中断
        System.out.println("Main thread is interrupted? " + Thread.interrupted());
        //②中断当前线程
        Thread.currentThread().interrupt();
        //③判断当前线程是否已经被中断
        System.out.println("Main thread is interrupted? " + Thread.currentThread().isInterrupted());

        try {
            //④当前线程执行可中断方法
            TimeUnit.MINUTES.sleep( 1);
        } catch (InterruptedException e) {
            //⑤捕获中断信号
            System.out.println( "I will be interrupted still." );
        }
        //通过运行上面的程序，你会发现，如果一个线程设置了interrupt标识，那么接下来的可中断方法会立即中断，
        // 因此注释⑤的信号捕获部分代码会被执行，请大家注意注释①和注释③中判断线程中断方法的不同，也希望读者结合本节的内容思考为什么要这么做?
    }






}
